#define _DEFAULT_SOURCE
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

#include "utils.h"


#define BUFSIZE     (32 * 1024)

#define CPUTIME_COLS    (sizeof(cputime_t)/sizeof(uint64_t))

typedef struct {
    uint64_t idle;
    uint64_t total;
    float usage;
} cpu_t;

// max: max cpu num
int parse_cpu(cpu_t *cpu, int max, char *buf)
{
    typedef union {
        struct {
            uint64_t user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;
        };
        uint64_t val[0];
    } cputime_t;

    char *ctx, *ctx_l;
    char *line, *word;
    int l = 0;

    ctx = buf;
    while ((line = strsep(&ctx, "\n")) != NULL) {
        if (!startswith(line, "cpu"))
            break;

        cputime_t tmp = {0};
        uint64_t total = 0;
        int w = 0;
        ctx_l = line;
        //printf("> %s\n", line);
        while ((word = strsep(&ctx_l, " ")) != NULL) {
            /* strsep will reture '\0' when continuous delim */
            if (*word == '\0')
                continue;

            /* such as: "cpu  3461 0 3626 12323494 199 0 250 0 0 0"
             * override first column "cpu", get number
             */
            if (w++ == 0)
                continue;

            uint64_t v = atoll(word);
            // printf("   > %lu\n", v);
            tmp.val[w-2] = v;

            total += v;
        }
        uint64_t idle = tmp.idle;

        /* calculate one cpu's usage */
        uint64_t delta_total = total - cpu[l].total;
        uint64_t delta_idle  = idle  - cpu[l].idle;

        cpu[l].usage = (delta_total - delta_idle) * 100.0 / delta_total;

        /* save for next calculate on same cpu */
        cpu[l].total = total;
        cpu[l].idle  = idle;

        l++;
    }

    return l;
}

#define HELP    \
    "cpustat: cpustat [option]\n" \
    "\n" \
    "Options:\n" \
    "   -d          delay seconds\n" \
    "   -h, --help  show help message\n"

int main(int argc, char *argv[])
{
    cpu_t cpu[64] = {0};

    char buf0[BUFSIZE];
    char buf1[BUFSIZE];

    if (argc == 2)
        if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
            goto help;

    int interval_us;
    if (argc == 1) {
        interval_us = 1 * 1000 * 1000;
    } else if (argc == 3) {
        if (strcmp(argv[1], "-d") == 0) {
            interval_us = (int)(atof(argv[2]) * 1000 * 1000);
            if (interval_us < 10 * 1000 || interval_us > 10 * 1000 * 1000) {
                pr_err("interval error: %d, (0.01~10s)\n", interval_us);
                return -1;
            }
        }
    } else {
        goto help;
    }

    load("/proc/stat", buf0, BUFSIZE - 1);
    usleep(interval_us);
    load("/proc/stat", buf1, BUFSIZE - 1);

    parse_cpu(cpu, 0, buf0);
    int cpu_num = parse_cpu(cpu, 0, buf1);

    int idx = 0;
    for (int i = 0; i < cpu_num; i++) {
        idx += sprintf(buf0 + idx, " %.1f", cpu[i].usage);
    }
    printf("CPU%s\n", buf0);

    return 0;

help:
    pr_err(HELP);
    return -1;
}
